/**
 * Copyright (C) 2011 Ron Coleman. Contact: ronncoleman@gmail.com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package bj.actor

import scala.actors.Actor
import scala.actors.Actor._
import scala.actors.OutputChannel
import bj.card.Hand
import bj.card.Card
import bj.util.Logs
import bj.hkeeping._
import bj.gui.HumanGui
import scala.actors.remote.RemoteActor
import scala.actors.remote.RemoteActor._

/**
 * @author Vikram
 *
 */

class HumanPlayer(glue: OutputChannel[Any], name: String, var bankRoll: Double, var betAmount: Double) extends Player(name, bankRoll, betAmount) {

  /** Assigning the port number */
  val PORT = 9000

  RemoteActor.classLoader = getClass.getClassLoader

  val humanSymbol = Symbol(pid.toString)

  /** dealer mail box */
  var dealer: OutputChannel[Any] = null

  override def act {
    /** Declare the port I'm listening on */
    alive(PORT)

    /** Register me */
    register(humanSymbol, self)

    debug("Human Symbol:" + humanSymbol)
    loop {
      react {
        case Go =>

          bet

        /** Receives a message from GUI about players new Bet amount */
        case NewBet(bet: Double) =>
          debug("Human Player placed new bet: " + bet)
          this.betAmount = bet

        /** Receives a message from House that the player has logged in and his pid and send it to GUI */
        case LoggedIn(pid: Int) =>
          debug("Logged In player :" + pid)
          glue ! LoggedIn(pid)

        /** Receives a card form the Dealer and forward it to GUI */
        case card: Card =>
          this.hit(card)
          debug(this + " received card " + card + " hand sz = " + cards.size + " value = " + value)
          glue ! card

        /** Receives the dealer's up-card and forward it to GUI */
        case Up(card) =>
          debug(this + " received dealer's up card = " + card)
          dealer = sender
          this.upcard = card
          glue ! Up(card)

        /** Receives the dealer's down-card and forward it to GUI  */
        case Hole(card) =>
          glue ! Hole(card)

        /** Receives DealerCard and forward it to GUI */
        case DealerCard(card, dealer) =>
          glue ! DealerCard(card, dealer)

        /** Receives Dealer broke message */
        case DealerBroke(size) =>
          debug(this + " received Dealer BROKE")
          glue ! DealerBroke(size)

        /** Receives broke message */
        case Broke =>
          debug(this + " received BROKE")
          glue ! Broke

        /** Receives message about dealt card to other players */
        case Observe(card, player, index) =>
          debug(this + " observed: " + card + " for the BetterPlayer " + player + " for hand " + index)
          if (glue != null) {
            glue ! Observe(card, player, index)
          }

        /** Receives Hit request from a HumanPlayer */
        case Hit(pid) =>
          debug(this + "player sending Hit request to Dealer and Player ID is:" + this.pid)
          dealer ! Hit(this.pid)

        /** Receives Stay request from a HumanPlayer */
        case Stay(pid) =>
          debug(this + "player sending Stay request to Dealer and Player ID is:" + this.pid)
          dealer ! Stay(this.pid)

        /** Receives DoubleDown request from a HumanPlayer */
        case DoubleDown(pid) =>
          debug(this + "player sending DoubleDown request to Dealer and Player ID is:" + this.pid)
          if (bankRoll >= (betAmount * 2)) {
            betAmount += betAmount
            dealer ! DoubleDown(this.pid)
          }

        /** Receives the table number I've been assigned to */
        case TableInfo(tid: Int, minBet: Double, maxBet: Double) =>
          debug(this + " received table assignment tid = " + tid)
          assign(tid, minBet, maxBet)
          this.tableId = tid

        /** Player got a BlackJack */
        case PlayerBlackJack =>
          debug(this + "Player got a blackjack)")
          glue ! PlayerBlackJack

        /** Dealer got a BlackJack */
        case DealerBlackJack =>
          debug(this + "Dealer got a blackjack)")
          glue ! DealerBlackJack

        /** Player got a BlackJack */
        case BetterPlayerBlackJack =>
          debug(this + "BetterPlayer got a blackjack)")
          glue ! BetterPlayerBlackJack

        /** Receives broke message */
        case BetterPlayerBroke =>
          debug(this + " received Better Player BROKE")
          glue ! BetterPlayerBroke

        /** Receives Better player details and forward it to GUI */
        case BotPlayerDetail(bet, bank) =>
          debug(this + " received Better Player bet: " + bet + " and bankroll: " + bank)
          glue ! BotPlayerDetail(bet, bank)

        /** Receives Better player startegy and forward it to GUI */
        case ActionBot(action, tableNo) =>
          debug("BotPlayer action =" + action + " and the tableId is :" + tableNo)
          glue ! ActionBot(action, tableNo)

        /** Receives the gain from Table if the player won the game and forward it to GUI for display */
        case Win(gain) =>
          val won = this.betAmount * gain
          bankRoll += won
          debug(this + " received WIN " + won + " new BankRoll = " + bankRoll)
          glue ! Win(bankRoll)

        /** Receives the gain from Table if the player Lost the game and forward it to GUI for display */
        case Lose(gain) =>
          val lost = this.betAmount * gain
          bankRoll += lost
          debug(this + " received LOSE " + lost + " new BankRoll = " + bankRoll)
          glue ! Lose(bankRoll)

        /** Receives the gain from Table if the player get push and forward it to GUI for display */
        case Push(gain) =>
          debug(this + " received PUSH BankRoll = " + bankRoll)
          glue ! Push(bankRoll)

        /** Receives an ACK */
        case Ok =>
          debug(this + " received Ok")

        /** Receives something completely from left field */
        case dontKnow =>
          /** Got something we REALLY didn't expect */
          debug(this + " received unexpected: " + dontKnow)
      }
    }
  }

  /** Overrides the method from the player and send the table details to the GUI */
  override def assign(tid: Int, minBet: Double, maxBet: Double) {
    sender ! Go
    glue ! TableInfo(tid, minBet, maxBet)
  }
}